创造无限可能 | 在 Android 12 中使用 widget
本文是 "更新 Android 12 中 widget" 系列的第二篇文章。在上一部分我们探讨了通过一些简单的方法,为 APP 用户呈现出非常显性的视觉更新。在这篇文章中,我们将一起了解一些更高级别的特性,这些功能会让您的 widget 更具交互性,更容易配置,并能在 Android 12 上呈现更好的 UI 体验。
更简单的配置
用户可重新设置原有 widget
xml/app_widget_info_checkbox_list.xml
<appwidget-provider
android:configure="com.example.android.appwidget.ListWidgetConfigureActivity"
android:widgetFeatures="reconfigurable"
... />
xml/app_widget_info_checkbox_list.xml
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/res/xml/app_widget_info_checkbox_list.xml
默认配置
ListAppWidget.kt
val layoutId = ListSharedPrefsUtil.loadWidgetLayoutIdPref(
context, appWidgetId
)
val remoteViews = if (layoutId == R.layout.widget_grocery_list) {
// 以 dp 为单位,指定最大宽度和高度,
// 并指定一个用于已指定尺寸的布局
val viewMapping = mapOf(
SizeF(150f, 150f) to constructRemoteViews(
R.layout.widget_grocery_list
), SizeF(250f, 150f) to constructRemoteViews(
R.layout.widget_grocery_grid
)
)
RemoteViews(viewMapping)
} else {
constructRemoteViews(
layoutId
)
}
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
ListAppWidget.kt
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/java/com/example/android/appwidget/ListAppWidget.kt
此时,该 widget 已被设定为 "提供默认配置",您需要将 configuration_optional flag 设置为 widgetFeatures 属性。这个操作会跳过额外的配置步骤,您可以直接在用户的主屏幕上呈现 widget。与此同时,请您确保添加 reconfigurable flag,以便用户后续可以更改生效的默认配置。
xml/app_widget_info_checkbox_list.xml
<appwidget-provider
android:configure="com.example.android.appwidget.ListWidgetConfigureActivity"
android:widgetFeatures="reconfigurable|configuration_optional"
... />
xml/app_widget_info_checkbox_list.xml
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/res/xml/app_widget_info_checkbox_list.xml
基于此更改,当用户将 widget 添加至主屏幕时,该 widget 会自动启用 Grocery List 布局。由于我们把配置活动添加至 appwidget-provider 的 configure 属性中,用户长按 widget 并点击编辑/重新设置按钮时,配置就会生效。
当用户配置该 widget 时,新的配置会被记录在 ListWidgetConfigureActivity 中。
ListWidgetConfigureActivity.kt
private fun onWidgetContainerClicked(@LayoutRes widgetLayoutResId: Int) {
ListSharedPrefsUtil.saveWidgetLayoutIdPref(this, appWidgetId, widgetLayoutResId)
// 配置活动有责任更新 app widget
val appWidgetManager = AppWidgetManager.getInstance(this)
ListAppWidget.updateAppWidget(this, appWidgetManager, appWidgetId)
// 请您确保回传原始的 appWidgetId
val resultValue = Intent()
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
setResult(RESULT_OK, resultValue)
finish()
}
ListWidgetConfigureActivity.kt
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/java/com/example/android/appwidget/ListWidgetConfigureActivity.kt
全新&改良版 API
Widget 的尺寸限制
除了现有的 minWidth、minHeigh、minResizeWidth 以及 minResizeHeight 以外,Android 12 还添加了新的 appwidget-provider 属性。
您可以使用新的 maxResizeWidth 和 maxResizeHeight 属性,来定义用户所能够调整的 widget 尺寸的最大高度和宽度。新的 targetCellWidth 和 targetCellHeight 属性能够定义设备主屏幕上的 widget 默认尺寸。
当定义了 targetCellWidth 和 targetCellHeight 属性后,搭载 Android 12 的设备将使用这些属性,而非 minWidth 和 minHeight。搭载 Android 11 及以下版本的设备将继续使用 minWidth 和 minHeight 属性。
提示: targetCellWidth 和 targetCellHeight 属性在 cells 中定义,而 maxResizeWidth 和 maxResizeHeight 属性是在 dps 中定义的。
xml/app_widget_info_checkbox_list.xml
<appwidget-provider
android:maxResizeWidth="240dp"
android:maxResizeHeight="180dp"
android:minWidth="180dp"
android:minHeight="110dp"
android:minResizeWidth="180dp"
android:minResizeHeight="110dp"
android:targetCellWidth="3"
android:targetCellHeight="2"
... />
xml/app_widget_info_checkbox_list.xml
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/res/xml/app_widget_info_checkbox_list.xml
响应式布局
val viewMapping: MutableMap<SizeF, RemoteViews> = mutableMapOf()
// 以 dp 为单位,指定最大宽度和高度,
// 并指定一个用于已指定尺寸的布局
val viewMapping = mapOf(
SizeF(150f, 110f) to RemoteViews(
context.packageName,
R.layout.widget_grocery_list
),
SizeF(250f, 110f) to RemoteViews(
context.packageName,
R.layout.widget_grocery_grid
),
)
appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))
//...
复合式按钮
在 Android 12 上,用户无需启动应用也能用 widget 做更多的事情啦!有了新的复合式按钮,您可以将 widget 变得更具交互性。这并不会改变 widget 的无状态特性,但您可以添加一个监听器来观察状态的变化。您可以调用 RemoteResponse.fromPendingIntent(),并在状态发生变化时向监听器发送一个 PendingIntent。
ItemsCollectionAppWidget.kt
remoteViews.setOnCheckedChangeResponse(
R.id.item_switch,
RemoteViews.RemoteResponse.fromPendingIntent(
onCheckedChangePendingIntent
)
)
ItemsCollectionAppWidget.kt
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/java/com/example/android/appwidget/ItemsCollectionAppWidget.kt
另一方面,如果 widget 有一个控件列表,不建议您在单项目 collection 上设置 PendingIntent,因为这会导致性能不佳。在这种情况下,您可以在该 collection 上设置一个 PendingIntent 模板,调起 RemoteResponse.fromFillInIntent(),并在状态发生变化时向监听器发送一个 fillInIntent。
ItemsCollectionAppWidget.kt
remoteViews.setOnCheckedChangeResponse(
R.id.item_switch,
RemoteViews.RemoteResponse.fromFillInIntent(
onCheckedChangeFillInIntent
)
)
ItemsCollectionAppWidget.kt
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/java/com/example/android/appwidget/ItemsCollectionAppWidget.kt
Collection 中简化版的 RemoteViews
ItemsCollectionAppWidget.kt
remoteViews.setRemoteAdapter(
R.id.items_list_view,
RemoteViews.RemoteCollectionItems.Builder()
.addItem(/* id= */ ID_1, RemoteViews(...))
.addItem(/* id= */ ID_2, RemoteViews(...))
//...
.setViewTypeCount(MAX_NUM_DIFFERENT_REMOTE_VIEWS_LAYOUTS)
.build()
)
ItemsCollectionAppWidget.kt
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/java/com/example/android/appwidget/ItemsCollectionAppWidget.kt
结语
https://github.com/android/user-interface-samples/tree/main/AppWidget
推荐阅读